import os
import time
import asyncio
import threading
import logging
from fastapi import FastAPI, Request
from contextlib import asynccontextmanager
import uvicorn

from motor.controller.api_client.node_manager_api_client import NodeManagerApiClient
from motor.utils.logger import get_logger
from motor.config.controller import controller_config as config
from motor.resources.http_msg_spec import RegisterMsg, ReregisterMsg, HeartbeatMsg, TerminateInstanceMsg
from motor.controller.core.instance_assembler import InstanceAssembler
from motor.controller.core.instance_manager import InstanceManager
from motor.controller.api_server import probe_api


logger = get_logger(__name__)


def validate_cert_and_key(cert_path: str, key_path: str):
    for path, desc in [(cert_path, '证书'), (key_path, '私钥')]:
        if not os.path.isfile(path):
            raise FileNotFoundError(f"{desc} file is not exist: {path}")
        with open(path, 'r') as f:
            first_line = f.readline().strip()
            if not first_line.startswith('-----BEGIN'):
                raise ValueError(f"{desc} file format is nor correct: {path}")


@asynccontextmanager
async def lifespan(app: FastAPI):
    enable_tls = os.environ.get("ENABLE_TLS", "0").lower() in ("1", "true", "yes")
    if enable_tls:
        cert_path = os.environ.get("CERT_PATH", "motor/config/server.crt")
        key_path = os.environ.get("KEY_PATH", "motor/config/server.key")
        try:
            validate_cert_and_key(cert_path, key_path)
            logger.info("Cert and key validate pass: %s, %s", cert_path, key_path)
        except Exception as e:
            logger.error("Cert or key validate failed: %s", e)
            raise
    logger.info("API server startup started")
    yield
    logger.info("API server shutdown completed")

app = FastAPI(lifespan=lifespan)


class HeartbeatAccessFilter(logging.Filter):
    """Suppress uvicorn access logs for /heartbeat unless level >= ERROR."""
    def filter(self, record: logging.LogRecord) -> bool:
        try:
            message = record.getMessage()
        except Exception:
            return True
        if record.name == "uvicorn.access" and "/heartbeat" in message:
            return record.levelno >= logging.ERROR
        return True

# Apply filter so /heartbeat access logs don't spam at INFO level
logging.getLogger("uvicorn.access").addFilter(HeartbeatAccessFilter())

app.include_router(probe_api.router)

@app.post("/controller/heartbeat")
async def heartbeat(request: Request):
    body = await request.json()
    try:
        hb_msg = HeartbeatMsg(**body)
    except Exception as e:
        logger.error("Failed to parse HeartbeatMsg: %s, body: %s", e, body)
        return {"error": "Invalid HeartbeatMsg format"}
    ret = InstanceManager().handle_heartbeat(hb_msg)
    return {"result": ret}

@app.post("/controller/register")
async def register(request: Request) -> dict:
    body = await request.json()
    try:
        register_msg = RegisterMsg(**body)
    except Exception as e:
        logger.error("Failed to parse RegisterMsg: %s, body: %s", e, body)
        return {"error": "Invalid RegisterMsg format"}
    ret = InstanceAssembler().register(register_msg)
    if ret == -1:
        return {"error": "Instance already registered"}
    else:
        return {"result": ret}

@app.post("/controller/reregister")
async def reregister(request: Request) -> dict:
    body = await request.json()
    try:
        reregister_msg = ReregisterMsg(**body)
    except Exception as e:
        logger.error("Failed to parse ReregisterMsg: %s, body: %s", e, body)
        return {"error": "Invalid ReregisterMsg format"}
    ret = InstanceAssembler().reregister(reregister_msg)
    if ret == -1:
        return {"error": "Instance already registered"}
    else:
        return {"result": ret}


@app.post("/controller/terminate-instance")
async def terminate_instance(request: Request) -> dict:
    body = await request.json()
    try:
        terminate_instance_msg = TerminateInstanceMsg(**body)
    except Exception as e:
        logger.error("Failed to parse TerminateInstanceMsg: %s, body: %s", e, body)
        # TODO: 参数不合法时，coordinator如何接收呢，建议返回非200的状态码或者返回值统一格式 {"code": 0,"data": {}, "message": ""}
        return {"error": "Invalid TerminateInstanceMsg format"}
    logger.warning("Terminate instance, reason: %s", terminate_instance_msg.reason)
    api_client = NodeManagerApiClient()
    api_client.stop(InstanceManager.get_instance(terminate_instance_msg.instance_id))
    return {"result": "Terminate instance succeed!"}


class ControllerAPI:
    def __init__(self, host: str = None, port: int = None):
        self.host = host if host is not None else config.controller_api_host
        self.port = port if port is not None else config.controller_api_port
        self.server = None
        self.loop = None
        self.api_server_thread = threading.Thread(
            target=self._run_api_server,
            daemon=True,
            name="APIServer"
        )
        self.api_server_thread.start()

    def _run_api_server(self) -> None:
        try:
            enable_tls = os.environ.get("ENABLE_TLS", "0").lower() in ("1", "true", "yes")
            logger.info("Starting API server on %s:%d TLS=%s", self.host, self.port, enable_tls)
            if enable_tls:
                cert_path = os.environ.get("CERT_PATH", "motor/config/server.crt")
                key_path = os.environ.get("KEY_PATH", "motor/config/server.key")
                server_config = uvicorn.Config(
                    app,
                    host=self.host, 
                    port=self.port, 
                    log_level="info", 
                    ssl_certfile=cert_path, 
                    ssl_keyfile=key_path
                )
            else:
                server_config = uvicorn.Config(app, host=self.host, port=self.port, log_level="info")
            self.server = uvicorn.Server(server_config)
            self.loop = asyncio.new_event_loop()
            asyncio.set_event_loop(self.loop)
            self.loop.run_until_complete(self.server.serve())
        except Exception as e:
            logger.error("API server error: %s", e)
        finally:
            if self.loop and not self.loop.is_closed():
                self.loop.close()

    def stop(self) -> None:
        if self.server and self.loop:
            try:
                future = asyncio.run_coroutine_threadsafe(self.server.shutdown(), self.loop)
                future.result(timeout=3)
                logger.info("API server stopped gracefully")
            except Exception as e:
                logger.error("Error stopping server: %s", e)
                if self.loop and not self.loop.is_closed():
                    self.loop.call_soon_threadsafe(self.loop.stop)
